package org.zeroes.common_prelude;
 import com.zeroes.cons.ZSGroup;
public class Quad {

	public float tlx;
	public float tly ;
	public float trx ;
	public float try_;	// is a keyword otherwise!
	public float brx ;
	public float bry ;
	public float blx ;
	public float bly ;
	
	public Quad(){
		this.tlx = 0;
		this.tly = 0;
		this.trx = 0;
		this.try_ = 0;	// is a keyword otherwise!
		this.brx = 0;
		this.bry = 0;
		this.blx = 0;
		this.bly = 0;
	}
	
	public void set_from_rect(Rect rc){
		
		this.tlx = rc.left;
		this.tly = rc.top;
		this.trx = rc.right;
		this.try_ = rc.top;
		this.brx = rc.right;
		this.bry = rc.bottom;
		this.blx = rc.left;
		this.bly = rc.bottom;
		
	}
	
	public void set_from_rotated_rect(Rect rc, float a){
		
		if (a == 0)
		{
			set_from_rect(rc);
		}
		else
		{
			float sin_a = (float)Math.sin(a);
			float cos_a = (float)Math.cos(a);

			float left_sin_a = rc.left * sin_a;
			float top_sin_a = rc.top * sin_a;
			float right_sin_a = rc.right * sin_a;
			float bottom_sin_a = rc.bottom * sin_a;

			float left_cos_a = rc.left * cos_a;
			float top_cos_a = rc.top * cos_a;
			float right_cos_a = rc.right * cos_a;
			float bottom_cos_a = rc.bottom * cos_a;
			
			this.tlx = left_cos_a - top_sin_a;
			this.tly = top_cos_a + left_sin_a;
			this.trx = right_cos_a - top_sin_a;
			this.try_ = top_cos_a + right_sin_a;
			this.brx = right_cos_a - bottom_sin_a;
			this.bry = bottom_cos_a + right_sin_a;
			this.blx = left_cos_a - bottom_sin_a;
			this.bly = bottom_cos_a + left_sin_a;
		}
		
	}
	
	public Quad offset(float px, float py){
		
		this.tlx += px;
		this.tly += py;
		this.trx += px;
		this.try_ += py;
		this.brx += px;
		this.bry += py;
		this.blx += px;
		this.bly += py;
		
		
		return this;
	}
	
	public Quad scale(float onX ,float onY,float aroundX, float aroundY, ZSGroup g){
		
		float gtx,gty;
		boolean free;
		free = (g.id.equals("freeEntities")) ? true: false;
		gtx = (free) ? 0:g.tx;
		gty = (free) ? 0:g.ty;
		
		Mat3 loriginTo = new Mat3(1,0,0,1,g.layer.centreX,g.layer.centreY);
		Mat3 ls= new Mat3(g.layer.lsx,0,0,g.layer.lsy,0,0);
		Mat3 loriginBack = new Mat3(1,0,0,1,-g.layer.centreX,-g.layer.centreY);
		
		Mat3 originTo = new Mat3(1,0,0,1,aroundX+gtx,aroundY+gty);
		Mat3 s= new Mat3(onX,0,0,onY,0,0);
		Mat3 originBack = new Mat3(1,0,0,1,-aroundX-gtx,-aroundY-gty);
		Mat3 tm=new Mat3(0,0,0,0,0,0);
		
		Mat3 goriginTo = new Mat3(1,0,0,1,g.gcx,g.gcy);
		Mat3 gs= new Mat3(g.gsx,0,0,g.gsy,0,0);
		Mat3 goriginBack = new Mat3(1,0,0,1,-g.gcx,-g.gcy);
	
		if(free){
			//tm=originTo.multwithMat(rot).multwithMat(originBack); asa era
				tm=loriginTo.multwithMat(ls).multwithMat(loriginBack).multwithMat(originTo).multwithMat(s).multwithMat(originBack);
			}
			else
			{
				//tm=goriginTo.multwithMat(grot).multwithMat(goriginBack).multwithMat(originTo).multwithMat(rot).multwithMat(originBack);
				tm=loriginTo.multwithMat(ls).multwithMat(loriginBack).multwithMat(goriginTo).multwithMat(gs).multwithMat(goriginBack).multwithMat(originTo).multwithMat(s).multwithMat(originBack);
			}
		// tm=originTo.multwithMat(s).multwithMat(originBack);
		
		float[] tl= new float[3];
		tl=tm.applyforPoint(tlx, tly);
		this.tlx=tl[0];
		this.tly=tl[1];
		
		float[] bl= new float[3];
		bl=tm.applyforPoint(blx, bly);
		this.blx=bl[0];
		this.bly=bl[1];
		
		float[] br= new float[3];
		br=tm.applyforPoint(brx, bry);
		this.brx=br[0];
		this.bry=br[1];
		
		float[] tr= new float[3];
		tr=tm.applyforPoint(trx, try_);
		this.trx=tr[0];
		this.try_=tr[1];
		
		return this;
	}
	
	public Quad rotateZ(float angle ,float aroundX, float aroundY, ZSGroup g){
		
		float gtx,gty;
		boolean free;
		free = (g.id.equals("freeEntities")) ? true: false;
		gtx = (free) ? 0:g.tx;
		gty = (free) ? 0:g.ty;
		
		Mat3 loriginTo = new Mat3(1,0,0,1,g.layer.centreX,g.layer.centreY);
		float lar=(float)Math.toRadians(g.layer.rotation);
		Mat3 lrot = new Mat3((float)Math.cos(lar),(float)Math.sin(lar),(float)-Math.sin(lar),(float)Math.cos(lar),0,0);
		Mat3 loriginBack = new Mat3(1,0,0,1,-g.layer.centreX,-g.layer.centreY);
		
		Mat3 originTo = new Mat3(1,0,0,1,aroundX+gtx+g.layer.tx,aroundY+gty+g.layer.ty);
		float ar=(float)Math.toRadians(angle);
		Mat3 rot = new Mat3((float)Math.cos(ar),(float)Math.sin(ar),(float)-Math.sin(ar),(float)Math.cos(ar),0,0);
		Mat3 originBack = new Mat3(1,0,0,1,-aroundX-gtx-g.layer.tx,-aroundY-gty-g.layer.ty);
		Mat3 tm=new Mat3(0,0,0,0,0,0);
		
		Mat3 goriginTo = new Mat3(1,0,0,1,g.gcx/*+g.layer.tx*/,g.gcy/*+g.layer.ty*/);
		float gar=(float)Math.toRadians(g.rotation);
		Mat3 grot = new Mat3((float)Math.cos(gar),(float)Math.sin(gar),(float)-Math.sin(gar),(float)Math.cos(gar),0,0);
		Mat3 goriginBack = new Mat3(1,0,0,1,-g.gcx/*-g.layer.tx*/,-g.gcy/*-g.layer.ty*/);
		
		
		if(free){
		//tm=originTo.multwithMat(rot).multwithMat(originBack); asa era
			tm=loriginTo.multwithMat(lrot).multwithMat(loriginBack).multwithMat(originTo).multwithMat(rot).multwithMat(originBack);
		}
		else
		{
			//tm=goriginTo.multwithMat(grot).multwithMat(goriginBack).multwithMat(originTo).multwithMat(rot).multwithMat(originBack);
			tm=loriginTo.multwithMat(lrot).multwithMat(loriginBack).multwithMat(goriginTo).multwithMat(grot).multwithMat(goriginBack).multwithMat(originTo).multwithMat(rot).multwithMat(originBack);
		}
		
		float[] tl= new float[3];
		tl=tm.applyforPoint(tlx, tly);
		this.tlx=tl[0];
		this.tly=tl[1];
		
		float[] bl= new float[3];
		bl=tm.applyforPoint(blx, bly);
		this.blx=bl[0];
		this.bly=bl[1];
		
		float[] br= new float[3];
		br=tm.applyforPoint(brx, bry);
		this.brx=br[0];
		this.bry=br[1];
		
		float[] tr= new float[3];
		tr=tm.applyforPoint(trx, try_);
		this.trx=tr[0];
		this.try_=tr[1];
		
		
		return this;
	}
	public Quad setfromQuad(Quad source){
		this.tlx = source.tlx;
		this.tly = source.tly;
		this.trx = source.trx;
		this.try_ = source.try_;
		this.brx = source.brx;
		this.bry = source.bry;
		this.blx = source.blx;
		this.bly = source.bly;
		
		return this;
		
	}
	
	public Rect bounding_box(){
		Rect rc = new Rect(0,0,0,0);
		
		rc.left =   (int)(Math.min(Math.min(this.tlx, this.trx), Math.min(this.brx, this.blx)));
		rc.top =    (int)(Math.min(Math.min(this.tly, this.try_), Math.min(this.bry, this.bly)));
		rc.right =  (int)(Math.max(Math.max(this.tlx, this.trx), Math.max(this.brx, this.blx)));
		rc.bottom = (int)(Math.max(Math.max(this.tly, this.try_), Math.max(this.bry, this.bly)));
		
		return rc;
	}
	
	public boolean contains_pt(double x, double y){
		
		double v0x = this.trx - this.tlx;
		double v0y = this.try_ - this.tly;
		double v1x = this.brx - this.tlx;
		double v1y = this.bry - this.tly;
		double v2x = x - this.tlx;
		double v2y = y - this.tly;

		double dot00 = v0x * v0x + v0y * v0y;
		double dot01 = v0x * v1x + v0y * v1y;
		double dot02 = v0x * v2x + v0y * v2y;
		double dot11 = v1x * v1x + v1y * v1y;
		double dot12 = v1x * v2x + v1y * v2y;

		double invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
		double u = (dot11 * dot02 - dot01 * dot12) * invDenom;
		double v = (dot00 * dot12 - dot01 * dot02) * invDenom;

		// Point is in first triangle
		if ((u >= 0.0) && (v > 0.0) && (u + v < 1))
			return true;

		// For second triangle, only v0 changes, so only recompute what that changes
		v0x = this.blx - this.tlx;
		v0y = this.bly - this.tly;

		 dot00 = v0x * v0x + v0y * v0y;
				 dot01 = v0x * v1x + v0y * v1y;
						 dot02 = v0x * v2x + v0y * v2y;
		invDenom = 1.0 / (dot00 * dot11 - dot01 * dot01);
		u = (dot11 * dot02 - dot01 * dot12) * invDenom;
		v = (dot00 * dot12 - dot01 * dot02) * invDenom;

		// Point is in second triangle
		return (u >= 0.0) && (v > 0.0) && (u + v < 1);
		
	}
	
	public double at(int i, boolean xory){
		i = i % 4;

		if (i < 0)
			i += 4;

		switch (i)
		{
			case 0: return xory ? this.tlx : this.tly;
			case 1: return xory ? this.trx : this.try_;
			case 2: return xory ? this.brx : this.bry;
			case 3: return xory ? this.blx : this.bly;
			default: return xory ? this.tlx : this.tly;
		}
	}
	
	public double midX(){
		return (this.tlx + this.trx  + this.brx + this.blx) / 4;
	}
	
	public double midY(){
		return (this.tlx + this.trx  + this.brx + this.blx) / 4;
	}
	
	public boolean intersects_quad(Quad rhs){
		// If rhs is completely contained by this quad, none of its segments intersect, but its
				// mid point will be inside this quad.  Test for this first.
				double midx = rhs.midX();
				double midy = rhs.midY();
				Util cr = new Util();
										
				if (contains_pt(midx,midy))
					return true;

				// Alternatively rhs may completely contain this quad
				midx = midX();
				midy = midY();
				
				if (rhs.contains_pt(midx, midy))
					return true;
					
				double a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y;

				// Otherwise check all 16 combinations of segment intersects
				int i, j;
				for (i = 0; i < 4; i++)
				{
					for (j = 0; j < 4; j++)
					{
						a1x = at(i, true);
						a1y = at(i, false);
						a2x = at(i + 1, true);
						a2y = at(i + 1, false);
						b1x = rhs.at(j, true);
						b1y = rhs.at(j, false);
						b2x = rhs.at(j + 1, true);
						b2y = rhs.at(j + 1, false);
						
						if (cr.segments_intersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y))
							return true;
					}
				}

				return false;
		
	}
}
